﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace System.Web.Mvc.Html
{
	public class HtmlTableBuilder<T>
	{
		private IEnumerable<HtmlTableRow<T>> _source;
		private HtmlTableColumnBuilder<T> _columns;
		private IDictionary<string, object> _tableAttributes;
		private HtmlTableRowBuilder<T> _rowBuilder;

		private string _newLine; 
		/// <summary>
		/// Constructor.
		/// </summary>
		/// <param name="source"></param>
		public HtmlTableBuilder(IEnumerable<T> source)
		{
			_newLine = Environment.NewLine;

			int rowNumber = 0;
			_source = source.Select(r => new HtmlTableRow<T> { RowData = r, RowNumber = rowNumber++ }).ToList();
		}

		/// <summary>
		/// Set a specific column builder with which to build the table header.
		/// </summary>
		/// <param name="columns"></param>
		internal void SetColumns(HtmlTableColumnBuilder<T> columns)
		{
			_columns = columns;
		}

		internal void SetRowAttributes(HtmlTableRowBuilder<T> builder)
		{
			_rowBuilder = builder;
		}

		internal void SetAttributes(IDictionary<string, object> htmlAttributes)
		{
			_tableAttributes = htmlAttributes;
		}

		/// <summary>
		/// Build entire table HTML.
		/// </summary>
		/// <returns></returns>
		internal MvcHtmlString Build()
		{
			var tableBuilder = new TagBuilder(HtmlTableTags.TABLE);
			tableBuilder.MergeAttributes(_tableAttributes);

			tableBuilder.InnerHtml += _newLine;
			tableBuilder.InnerHtml += BuildColumns();

			foreach (var row in _source)
			{
				tableBuilder.InnerHtml += BuildDataRow(row);
				tableBuilder.InnerHtml += _newLine;
			}

			return MvcHtmlString.Create(tableBuilder.ToString());
		}

		/// <summary>
		/// Build table header HTML.
		/// </summary>
		private string BuildColumns()
		{
			if (_columns != null && _columns.Any())
			{
				var rowBuilder = new TagBuilder(HtmlTableTags.TABLE_ROW);
				rowBuilder.InnerHtml += _newLine;

				foreach (var column in _columns)
				{
					rowBuilder.InnerHtml += BuildHeaderCell(column.DisplayName);
					rowBuilder.InnerHtml += _newLine;
				}

				return rowBuilder.ToString();
			}
			else
			{
				return String.Empty;
			}
		}

		private string BuildDataRow(HtmlTableRow<T> row)
		{
			var rowBuilder = new TagBuilder(HtmlTableTags.TABLE_ROW);
			rowBuilder.InnerHtml += _newLine;

			if (_rowBuilder != null && (_rowBuilder.Predicate == null || _rowBuilder.Predicate(row)))
			{
				rowBuilder.MergeAttributes(_rowBuilder.Attributes);
			}

			foreach (var col in _columns)
			{
				var val = col.ColumnValueFunction(row.RowData);
				rowBuilder.InnerHtml += BuildDataCell(val);
				rowBuilder.InnerHtml += _newLine;
			}

			return rowBuilder.ToString();
		}

		private string BuildHeaderCell(string headerValue)
		{
			var cellBuilder = new TagBuilder(HtmlTableTags.TABLE_HEADER_CELL);
			cellBuilder.InnerHtml = headerValue;
			return cellBuilder.ToString();
		}

		private string BuildDataCell(object cellValue)
		{
			var cellBuilder = new TagBuilder(HtmlTableTags.TABLE_DATA_CELL);
			cellBuilder.InnerHtml = cellValue.ToString();
			return cellBuilder.ToString();
		}
	}

	public class HtmlTableRow<T>
	{
		public int RowNumber { get; set; }

		public T RowData { get; set; }
	}

	internal class HtmlTableRowBuilder<T>
	{
		internal Func<HtmlTableRow<T>, bool> Predicate { get; set; }

		internal IDictionary<string, object> Attributes { get; set; }
	}
}
